Walmart es la cadena de retail más grande en el mundo; fue fundada por Sam Walton 1962 y desde entonces ha tenido un gran crecimiento llegando a cotizar en la bolsa de Nueva York en 1972. Actualmente Walmart ha entendido el valor de la información y con sus 20,000 tiendas en 28 países entiende que los datos que generan están muy poco aprovechados. Con esto en mente Walmart ha creado una nube privada que enfrenta alrededor de 2.5 Petabytes de información cada hora y ha creado también “The Data Café” que es un hub punta de lanza en analítica con la finalidad de aprovechar de mejor manera esa cantidad de información. La necesidad de encontrar insights que puedan accionar cambios es lo que está moviendo a la empresa y sus esfuerzos en el mediano y largo plazo.
En el presente proyecto, se realizó un modelo para categorizar las visitas que realizan los clientes a Walmart basándose únicamente en los productos que compran los clientes en su visita. La base se obtuvo de kaggle de la competencia titulada “Walmart Recruiting: Trip Type Classification”.
Las categorías están representadas por números enteros no contiguos, y no se conoce el significado de ninguna categoría.
La función que se busca optimizar, más específicamente minimizar, es la pérdida logarítmica multiclase también conocida como log loss multiclase, misma que se define como:
\(-\frac{1}{N}\sum_{i=1}^{N}\sum_{j=1}^{M}y_{ij}log(p_{ij})\)
donde \(N\) es el total de visitas en el conjunto de prueba, \(M\) es el total de categorías existentes (como más adelante se verá \(M = 38\)), \(y_{ij}\) es 1 si la observación \(i\) pertenece a la categoría \(j\) y 0 en otro caso, \(p_{ij}\) es la probabilidad obtenida por el modelo que la observación \(i\) pertenezca a la categoría \(j\).
Obtener un valor de la función log loss menor a 1 sobre el conjunto de prueba.
Se realiza la lectura y limpieza de datos con las funciones utils, load, prepare y clean:
source("utils.R")
source("00-load.R")
## [1] "walmart.rds se bajó y guardó\n"
source("01-prepare.R")
source("02-clean.R")
La base de datos original muestra un registro por cada producto que se compró en las diferentes visitas de los clientes en la tienda. Por ejemplo, si un cliente en su visita compró 10 productos diferentes, esa visita tiene 10 registros con el mismo identificador de visita y mismo tipo de visita. Los primeros renglones de la base son los siguientes:
## TripType VisitNumber Weekday Upc ScanCount DepartmentDescription
## 1 999 5 Friday 68113152929 -1 FINANCIAL SERVICES
## 2 30 7 Friday 60538815980 1 SHOES
## 3 30 7 Friday 7410811099 1 PERSONAL CARE
## 4 26 8 Friday 2238403510 2 PAINT AND ACCESSORIES
## 5 26 8 Friday 2006613744 2 PAINT AND ACCESSORIES
## 6 26 8 Friday 2006618783 2 PAINT AND ACCESSORIES
## FinelineNumber
## 1 1000
## 2 8931
## 3 4504
## 4 3565
## 5 1017
## 6 1017
Las variables con las que se cuenta en la base de datos son:
La estructura original de la base no tiene registros únicos a nivel visita, esto complica el ajuste del modelo pues las categorías son a nivel visita. Si se trabajara con la base original, se estaría ajustando una categoría a nivel producto en lugar de a nivel visita. Por lo tanto, el tratamiento que se realizó a los datos consistió en construir la base de forma que tenga un solo renglón por visita. La variable de día de la semana es a nivel visita, por lo que no sufrió modificaciones. Respecto a las variables del departamento y número de productos, se crearon tantas columnas como departamentos existen, 68 en total, y se sumaron de acuerdo al número de productos por departamento comprados por visita. La base creada y utilizada para modelar luce como sigue:
## # A tibble: 10 x 71
## # Groups: TripType, VisitNumber, Weekday [10]
## TripType VisitNumber Weekday HR_PHOTO ACCESSORIES AUTOMOTIVE BAKERY
## <int> <int> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 3 106 Friday 0 0 0 0
## 2 3 121 Friday 0 0 0 0
## 3 3 153 Friday 0 0 0 0
## 4 3 162 Friday 0 0 0 0
## 5 3 164 Friday 0 0 0 0
## 6 3 177 Friday 0 0 0 0
## 7 3 181 Friday 0 0 0 0
## 8 3 188 Friday 0 0 0 0
## 9 3 203 Friday 0 0 0 0
## 10 3 265 Friday 0 0 0 0
## # ... with 64 more variables: BATH_AND_SHOWER <dbl>, BEAUTY <dbl>,
## # BEDDING <dbl>, BOOKS_AND_MAGAZINES <dbl>, BOYS_WEAR <dbl>,
## # BRAS__SHAPEWEAR <dbl>, CAMERAS_AND_SUPPLIES <dbl>,
## # CANDY_TOBACCO_COOKIES <dbl>, CELEBRATION <dbl>, COMM_BREAD <dbl>,
## # CONCEPT_STORES <dbl>, COOK_AND_DINE <dbl>, DAIRY <dbl>,
## # DSD_GROCERY <dbl>, ELECTRONICS <dbl>, FABRICS_AND_CRAFTS <dbl>,
## # FINANCIAL_SERVICES <dbl>, FROZEN_FOODS <dbl>, FURNITURE <dbl>,
## # GIRLS_WEAR_46X__AND_74 <dbl>, GROCERY_DRY_GOODS <dbl>, HARDWARE <dbl>,
## # HEALTH_AND_BEAUTY_AIDS <dbl>, HOME_DECOR <dbl>, HOME_MANAGEMENT <dbl>,
## # HORTICULTURE_AND_ACCESS <dbl>, HOUSEHOLD_CHEMICALS_SUPP <dbl>,
## # HOUSEHOLD_PAPER_GOODS <dbl>, IMPULSE_MERCHANDISE <dbl>,
## # INFANT_APPAREL <dbl>, INFANT_CONSUMABLE_HARDLINES <dbl>,
## # JEWELRY_AND_SUNGLASSES <dbl>, LADIES_SOCKS <dbl>, LADIESWEAR <dbl>,
## # LARGE_HOUSEHOLD_GOODS <dbl>, LAWN_AND_GARDEN <dbl>,
## # LIQUORWINEBEER <dbl>, MEAT__FRESH__FROZEN <dbl>,
## # MEDIA_AND_GAMING <dbl>, MENSWEAR <dbl>, `NULL` <dbl>,
## # OFFICE_SUPPLIES <dbl>, OPTICAL__FRAMES <dbl>, OPTICAL__LENSES <dbl>,
## # OTHER_DEPARTMENTS <dbl>, PAINT_AND_ACCESSORIES <dbl>,
## # PERSONAL_CARE <dbl>, PETS_AND_SUPPLIES <dbl>, PHARMACY_OTC <dbl>,
## # PHARMACY_RX <dbl>, PLAYERS_AND_ELECTRONICS <dbl>,
## # PLUS_AND_MATERNITY <dbl>, PRE_PACKED_DELI <dbl>, PRODUCE <dbl>,
## # SEAFOOD <dbl>, SEASONAL <dbl>, SERVICE_DELI <dbl>,
## # SHEER_HOSIERY <dbl>, SHOES <dbl>, SLEEPWEAR_FOUNDATIONS <dbl>,
## # SPORTING_GOODS <dbl>, SWIMWEAR_OUTERWEAR <dbl>, TOYS <dbl>,
## # WIRELESS <dbl>
En cuanto a la limpieza, ésta consistió en unificar dos departamentos MENS WEAR con MENSWEAR; para todas las variables de departamento se cambiaron los NA’s por 0, pues en realidad los valores nulos de esas variables son porque se realizaron cero compras de ese departamento en la respectiva visita. En los nombres de las variables, se sustituyeron los espacios por guiones bajos y se eliminaron caracteres especiales (las comas y los &) para facilitar el manejo de la base en el análisis exploratorio.
De esta manera, se obtiene una base de datos con 95’674 registros (únicos a nivel visita) y 69 variables explicativas.
En primer lugar, se analiza la variable objetivo: el tipo de visita realizada en Walmart. Su distribución luce como se muestra en la siguiente gráfica:
Se observa que existen categorías que sobresalen respecto a las demás, estas categorías son: 8, 9, 39, 40 y 999. Por el contrario, existen categorías que tienen muy poca presencia en los datos, por ejemplo la 12, 14 y 23, estas categorías con poca representación en los datos muy probablemente serán complicadas de modelar. Para tener mayor detalle se obtienen las frecuencias por categoría:
## 3 4 5 6 7 8 9 12 14 15 18 19
## 3643 346 4593 1277 5752 12161 9464 269 4 978 549 375
## 20 21 22 23 24 25 26 27 28 29 30 31
## 637 641 928 139 2609 3698 503 785 492 433 1081 594
## 32 33 34 35 36 37 38 39 40 41 42 43
## 1984 1315 719 2030 3005 2788 2912 9896 6130 583 1858 872
## 44 999
## 1187 8444
Se realiza ahora el análisis univariado sobre las variables explicativas.
El día de la semana tiene la siguiente distribución:
Notoriamente, la mayoría de las visitas se realizan entre el viernes y el domingo, principalmente el fin de semana. Resulta curioso que el día de la semana con menos visitas es el jueves.
Las visitas realizadas cada día de la semana son las siguientes:
## Monday Tuesday Wednesday Thursday Friday Saturday Sunday
## 12027 11530 11612 11243 15234 16904 17124
El departmento al cual pertenecen los productos comprados y devueltos tiene la siguiente distribución:
Como era de esperarse, existen departamentos con mucha población y departamentos con escasa población.
Es importante recordar que la estructura de la base transformada tiene una columna por cada departamento, por lo tanto la distribución real de cada variable de departamento tiene distribuciones muy concentradas en el cero, por ejemplo:
Incluso en el departamento más pobaldo GROCERY DRY GOODS, la distribución está bastante concentrada en el cero:
Resulta complicado visualizar las gráficas de la variable objetivo con las variables explicativas, pues el tipo de visita puede tomar 38 valores diferentes. Por ello, se realizarán gráficas separadas, mostrando de 10 en 10 las categorías.
La distribución del día de la semana en que se realiza la visita, para cada categoría se muestra a continuación:
Teniendo en mente la gráfica univariada del día de la semana que se mostró en la sección anterior, y contrastando con la distribución de esta variable para cada categoría, se pueden detectar patrones de cada uno de los tipos de visita. Mencionaremos algunos de ellos:
En la categoría 3, la distribución del día de la semana es bastante diferente a la global, pues tienen un nivel bajo el lunes, comienza a subir, disimuye el jueves y llega a su máximo en viernes, descendiendo el fin de semana.
En la categoría 25, las visitas a Walmart tienen su menor número en lunes, y van a aumentando día a día llegando a su máximo el domingo.
Para la categoría 40, el patrón es igual al global, pero las diferencias del nivel en los diferentes días es más acentuada que en la distribución global.
Es importante mencionar, que todos los tipos de vísita alcanzan su máximo entre el viernes y el domingo.
Respecto a la variable objetivo y los distintos departamentos, nuevamente se separa en 4 gráficas y se acotó el rango del eje x de -1 a 10 para facilitar su entendimiento e interpretación.
La gráfica bivariada de la variable objetivo y el departamento con mayores compras GROCERY DRY GOODS se muestra a continuación:
Se observa que en la mayoría de las categorías, el departamento GROCERY DRY GOODS está concenctrado en el cero, pero existen categorías con distribuciones que muestran concentraciones en valores positivos. Por ejemplo 12, 15, 44 y más notoriamente en las categorías 37, 38, 39 y 40. Se puede pensar que estas categorías pueden referirse a visitas a Walmart orientadas a compras de este tipo de productos.
Ahora se analiza la distribución del departamento DSD GROCERY para cada tipo de categoría:
Este departamento resulta ser más interesante, pues tiene muchas menos categorías concentradas en el cero. Por ejemplo, la categoría 35 parece tener muy poca población en el cero a diferencia del departamento que se mostró anteriormente.
Ahora se realiza el análisis sobre un departamento menos poblado que los anteriores, éste es PHARMACY OTC:
En este departamento, llaman mucho la atención las categorías 4 y 5 pues son las que tienen una distribución más alejada del cero. Esto lleva a pensar que estas categorías están ligadas con la compra de productos farmacéuticos.
El análisis se vuelve más complicado cuando los departamentos están poco poblados, por ejemplo en JEWELRY AND SUNGLASSES, las gráficas son las siguientes:
En este departamento se redujo más el rango del eje x, de -1 a 5 para tener mejor visualización. La distribución de joyería y lentes de sol tiene una altísima concentración en el cero para prácticamente todas las categorías.
Dada la cantidad de variables con las que se cuenta y los 38 valores que toma del tipo de visita, el análisis multivariado tiene muchas posibles combinaciones. A continuación se muestran algunas de ellas.
Se puede obtener por ejemplo, la distribución conjunta de 2 departamentos con la variable objetivo (para facilitar la visualización, se separan por grupos de categorías):
Resulta difícil interpretar las gráficas para las categorías poco pobladas. Cuando las categorías están más pobladas, por ejemplo en la 37, se observa que estos dos departamentso PRODUCE y DSD GROCERY, están dispersos el primero en valores menores a 20 y el segundo en valores menores a 10. Por el contrario en la categoría 40, los valores de PRODUCE son menores a 15 y los valores de DSD GROCERY son menores a 25.
En cuanto a la ingeniería de características, la construcción de la base a nivel visita fue el paso esencial, pues permite encontrar un modelo más acertivo, y mas que limpieza de datos fue feature engineering . La construcción de las variables de productos comprados y/o devueltos por departamento también formaron parte del feature engineering.
Adicionalmente, se construyeron las siguientes variables a partir de las disponibles originalmente:
Total de productos comprados en la visita
Indicadora de devolución de productos, vale 1 si se devolvió al menos un producto en la visita y 0 en otro caso
Total de productos devueltos en la visita
Total de departamentos visitados, si se compró al menos un producto en el departamento éste cuenta como 1 en la suma de departamentos visitados
Productos diferentes comprados respecto a UPC, es decir cuántos productos con diferente código UPC se compraron en la visita
Día del mes en que se realizó la visita, se explica más adelante esta variable
El total de productos comprados tiene la siguiente distribución:
En la mitad de las visitas realizadas, los clientes compran 4 productos, éste es el valor de su mediana:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -52.000 2.000 4.000 7.499 9.000 311.000
La indicadora devolución de productos luce como sigue:
Aproximadamente, en el 11.6% de las visitas se realiza alguna devolución de producto. Este porcentaje puede considerarse alto.
La distribución de productos devueltos está concentrada en el cero, pues como se vio en la indicadora, en la mayoría de las visitas no se devuelven productos.
De la distribución de la variable Departamentos visitados se concluye que en la mayoría de las visitas los clientes compran productos de máximo 5 departamentos diferentes:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 1.000 2.000 3.337 5.000 26.000
En cuanto al número de productos con diferente UPC, la mayoría de los clientes compran en su visita productos de máximo 8 códigos UPC distintos.
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 2.000 3.000 6.602 8.000 207.000
Sobre la variable del día del mes, se realizó el siguiente análisis para llegar a su construcción: Al juntar las bases train y test, ordenarlas por el código de visita y graficar la distribución de la variable día de la semana se observó la siguiente gráfica:
En esta gráfica, el valor 1 en el eje y representa el viernes, el valor 2 el sábado, así sucesivamente hasta el valor 7 que representa al jueves. Al observarla, lleva a pensar que de alguna forma, los códigos de visita están ligados con el tiempo, parece ser que se trata de 31 días iniciando con un jueves y finalizando en domingo.
Por lo tanto se construyó la variable día del mes, numerando de 1 a 31 los diferentes días de la semana de acuerdo a su código de visita. Su distribución es la siguiente:
Finalmente en R, se guardan los datos como un archivo feather para poder realizar el ajuste de modelos en Python.
path <- "Datos/walmart.feather"
write_feather(walmartSpread, path)
Todos los modelos se calcularon en Python, siguiendo esta metodología:
Se ajustaron los siguientes modelos: regresión logística multinomial, bosques aleatorios, máquinas de soporte vectorial, red neuronal. El desempeño final (evaluación de kaggle) de cada uno fue:
| Modelo | Parámetros | Tiempo Ajuste | Public Score |
|---|---|---|---|
| Regresión Multinomial | penalización L1, C = 1 | 3 hrs | 1.3185 |
| Bosque Aleatorio | depth = 100, feat = sqrt, split = 10, n_est = 1500 | 4 hrs | 2.1320 |
| Máquina de Soporte Vectorial | kernel lineal, C = 10 | 5 hrs | 1.0820 |
| Red Neuronal | alpha 100, 30 capas, 1000 iter, semilla 0 | 4 hrs | 1.0370 |
El mejor ajuste se obtuvo con la red neuronal consiguiendo un log loss muy cercano a 1, es importante mencionar que la máquina de soporte tuvo un desempeño muy similar. Nuestro mejor modelo nos situaría en el lugar 445 del total de 1047 participantes en la competencia, esto es el percentil 42%.
Por último, se ajustó un modelo siguiendo la página http://kaslemr.github.io/Walmart_Kaggle_Competition/, donde el competidor menciona el método final que utilizó para ajustar su modelo.
El competidor utilizó la liberaría XGB la cual contiene un algoritmo de gradient boosting. Este algoritmo consiste en ensambles de árboles de decisión. La diferencia de esta liberería respecto a otras de ensambles, es el parámetro para forzar a que termine el modelo después de no mejorar en cierto número de iteraciones. De esta manera evita el sobreajuste y su tiempo de entretanmiento suele ser bajo.
El modelo se ajustó siguiendo su código, parte la base de entrenamiento en train y en test para la construcción del modelo, sin usar validación cruzada ni optimización de hiperparámetros. Tardó 20 minutos en terminar de ajustarse. El desempeño final (kaggle) que se obtuvo fue de 0.9647, logrando así la métrica de éxito (log loss menor a 1), lo cual nos situaría en la posición 411, percentil 39%.